/*
==========================================================
DX490a - Summer 2010
Instructor: Stelios Manousakis
==========================================================
Class 10.1:
Patterns in the server: Analog-style control signals and Demand UGens
Contents:
• Analog-style control signals
- Counters
> PulseCount
> Stepper
> PulseDivider
- Selectors
> Select
- Sample-and-Hold
• Demand UGens
- a list of Demand UGens (June 2010)
==========================================================
*/
// ================= ANALOG-STYLE CONTROL SIGNALS =================
// SynthDefs don't have to be completely static: you can schedule things using analog-studio-style controls inside a synthdef, sequencing signals with signals - there are a few different UGens to do that. Most importantly, however, and breaking out from the analog paradigm, you can use Demand UGens to create signal patterns.
// ====== COUNTERS ======
// ------ PulseCount --
// PulseCount is the simplest, most basic signal counter. You provide it with a trigger and it starts counting integers from 0, until you reset it:
SynthDef("help-PulseCount",{ arg out=0;
Out.ar(out,
SinOsc.ar(
(PulseCount.ar(Impulse.ar(8), Impulse.ar(1.2)).poll * 200) + 100,
0, 0.25
)
)
}).play;
// ------ Stepper --
// Stepper is a counter that you can trigger and reset with a signal like PulseCount, except you can also define the minimum and maximum values, as well as the step size.
SynthDef("help-Stepper",{ arg out=0;
Out.ar(out,
SinOsc.ar(
Stepper.kr(
Impulse.kr(7), // trigger
0, // reset
5, // min value
17, // max value
1, // step value
nil) // reset value
* 100,
0, 0.25
)
)
}).play;
// much nicer when resetting and using a big enough step to make the pattern foldover:
SynthDef("help-Stepper",{ arg out=0;
Out.ar(out,
SinOsc.ar(
Stepper.kr(
Impulse.kr(7), // trigger
Impulse.kr(0.25), // reset
5, // min value
17, // max value
5, // step value
3) // reset value
* 100,
0, 0.25
)
)
}).play;
// ------ PulseDivider --
// PulseDivider does what its name says: outputs an impulse after receiving a specified number of triggers in its input.
// Here 's a cool little example by the ixi people
(
SynthDef(\drummer, { arg out=0, tempo=4, amp = 1;
var snare, base, hihat;
tempo = Impulse.ar(tempo); // for a drunk drummer replace Impulse with Dust !!!
snare = WhiteNoise.ar(Decay2.ar(PulseDivider.ar(tempo, 4, 2), 0.005, 0.5));
base = SinOsc.ar(Line.ar(120,60, 1), 0, Decay2.ar(PulseDivider.ar(tempo, 4, 0), 0.005, 0.5));
hihat = HPF.ar(WhiteNoise.ar(1), 10000) * Decay2.ar(tempo, 0.005, 0.5);
Out.ar(out, ((snare + base + hihat) * 0.4!2) * amp)
}).load(s);
)
a = Synth(\drummer);
a.set(\tempo, 6);
a.set(\tempo, 18, \amp, 0.75);
a.set(\tempo, 180, \amp, 0.2); // check the CPU! no increase.
// ====== SELECTORS ======
// The Select UGens are very handy for switching or mixing between members of an array; these can be sequences of values, or even different UGens. Be warned though, as this is server-side, all the UGens are constantly running, regardless of wether you can hear them or not.
// ------ Select --
// Here is yet one more not very musical example (but you get the idea):
SynthDef(\SelectSeq,{ arg outbus = 0;
var array;
array = [LFSaw.kr(1), LFPulse.kr(3), LFDNoise1.kr(2), LFTri.kr(0.5), LFPulse.kr(5), DC.ar(0.5)];
Out.ar(outbus,
SinOsc.ar(
(SelectX.kr(LFDNoise0.kr(5) * array.size, array) * 440).abs + 220,
0, 0.25
)
)
}).play;
// Or, going through an array instead of using UGens
SynthDef(\SelectSeq,{ arg outbus = 0;
var array;
array = Array.series(12, 60, 1).midicps; // create an octave starting at middle C
Out.ar(outbus,
SinOsc.ar(
Select.kr(LFDNoise0.kr(5) * array.size, array),
0, 0.25)
)
}).play;
// There is also SelectX, which lets you mix and crossfade between adjacent member of the array. Also, you may want to check out [SelectL], [RotateN], and [RotateL], all part of the wslib quark.
// ====== SAMPLE-AND-HOLD ======
// This is the most 'traditional', analog studio technique: having a signal run and using another signal to sample it at regular or irregular intervals. The SC object that does that is called Latch. Latch is audio rate, and the signal it samples also needs to be audio rate.
SynthDef(\sampleAndHold,{ arg outbus = 0;
Out.ar(outbus,
SinOsc.ar(
Latch.ar(WhiteNoise.ar(440, 220) , Impulse.ar(7)),
0, 0.25)
)
}).play;
// Let's make the trigger signal a bit more interesting (this is the very basics of a clock idea I developed in the analog studio at the Institute of Sonology):
SynthDef(\sampleAndHold,{ arg outbus = 0;
var trig;
trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness
Out.ar(outbus,
SinOsc.ar(
Latch.ar(WhiteNoise.ar(440, 220), trig),
0, 0.25)
)
}).play;
// ================= DEMAND UGENS =================
// With Demand UGens you can implement pattern/stream-style behavior in the server, inside a SynthDef. You provide a trigger and get a signal output calculated from the Demand UGen. There is quite a few different behaviors implemented, with several Demand UGens existing in SC - their names begin with 'D' so they are easy to spot. What's nice about them, is that they are very efficient, as they use a 'lazy' implementation, and you can get specific values out of them without worrying about the timing of your triggers compared to your control signal (which would not be the case with a more standard sample-and-hold technique). Also, there is quite a lot of options.
// The fundamental UGen here is Demand. This is the UGen that you need to use to get data from the rest of the Demand UGens. You provide it a trigger, and it demands a value form the UGen or UGens in its list. You can also reset it, in which case Demand will reset all the UGens it contains. Demand is essentially a Sample-and-Hold mechanism, but it is different than Latch in that it can handle all the Demand UGen bureaucracy.
// Here is the same example as above, but rewritten with Demand instead of Latch:
SynthDef(\sampleAndHold,{ arg outbus = 0;
var trig;
trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness
Out.ar(outbus,
SinOsc.ar(
Demand.ar(trig, 0, WhiteNoise.ar(440, 220)),
0, 0.25)
)
}).play;
// Now, using some of the Demand UGens:
// • Dseries: generate arithmetic series
SynthDef(\demand,{ arg outbus = 0;
var trig, pat;
trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness
pat = Dseries(0, 1, inf) % 13; // generate an infinite series of values, starting from 0 and with a step-size of 1, and have them fold between 0 and 13
Out.ar(outbus,
SinOsc.ar(
Demand.ar(trig, 0, (pat * 440) + 220),
0, 0.25)
)
}).play;
// • Dgeom: generate geometric series
SynthDef(\demand,{ arg outbus = 0;
var trig, pat;
trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness
pat = Dgeom(1, 1.2, inf) % 13; // generate an infinite series of values, starting from 0 and with a step-size of 1, and have them fold between 0 and 13
Out.ar(outbus,
SinOsc.ar(
Demand.ar(trig, 0, (pat * 440) + 220),
0, 0.25)
)
}).play;
// now, let's use Demand's reset input to bring it back:
SynthDef(\demand,{ arg outbus = 0;
var trig, pat;
trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness
pat = Dgeom(1, 1.2, inf) % 13; // generate an infinite geometric series of values, starting from 0 and growing by 1.2, and have them fold between 0 and 13
Out.ar(outbus,
SinOsc.ar(
Demand.ar(trig, Dust.ar(0.25), (pat * 440) + 220),
0, 0.25)
)
}).play;
// • Dseq: play back a sequence
SynthDef(\demand,{ arg outbus = 0;
var trig, pat;
trig = Impulse.ar(7) - Impulse.ar(2) + Dust.ar(0.5); // the second impulse generator will remove some of the triggers, and the Dust will add a bit of randomness
pat = Dseq([1, 4, 5, 2, 7, 5, 8], inf); // play back the sequence infinite times
Out.ar(outbus,
SinOsc.ar(
Demand.ar(trig, 0, (pat * 440) + 220),
0, 0.25)
)
}).play;
// • Dshuf: shuffle a sequence
// Now, for a more complex example, using two demand ugens, one embedded in the other:
SynthDef(\demand,{ arg outbus = 0;
var trig, pat;
trig = Impulse.ar(7); // simple clock to hear what's going on
pat = Dseq([Dshuf([1, 4, 5, 2, 7, 5, 8], 4)], inf); // play back the shuffled sequence four times, then play another shuffle; repeat infinite times
Out.ar(outbus,
SinOsc.ar(
Demand.ar(trig, 0, (pat * 440) + 220),
0, 0.25)
)
}).play;
// • now, for an even more complex example, using Dbrown, Dstutter and Dwhite as well
SynthDef(\demand,{ arg outbus = 0;
var trig, pat;
trig = Impulse.ar(7); // simple clock to hear what's going on
pat = Dseq(
[Dshuf(
[1, 4, 5, 2, 7, 5, 8], 3),
Dbrown(1, 12, 1, 7),
Dstutter(3, Dwhite(1, 12, 7))],
inf); // play back the shuffled sequence three times, then play another seven values from dbrown; repeat infinite times
Out.ar(outbus,
SinOsc.ar(
Demand.ar(trig, 0, (pat * 440) + 220),
0, 0.25)
)
}).play;
// • Duty and TDuty have their own triggering system, so you can use another Demand UGen to specify a stream of durations
SynthDef(\demand,{arg outbus = 0;
var freq;
freq = Duty.kr(
Drand([0.01, 0.2, 0.4], inf), // demand ugen as durations
0,
Dseq([204, 400, 201, 502, 300, 200], inf)
);
Out.ar(outbus, SinOsc.ar(freq * [1, 1.01]) * 0.1);
}).play;
// Demand UGens can run at audio rate as well:
SynthDef(\demand,{arg outbus = 0;
var freq;
freq = Duty.kr(
Drand([0.01, 0.2, 0.4] * MouseX.kr(0.001, 3, 1), inf), // demand ugen as durations
0,
Dseq([204, 400, 201, 502, 300, 200], inf)
);
Out.ar(outbus, SinOsc.ar(freq * [1, 1.01]) * 0.1);
}).play;
// ------ Demand UGen list (June 2010) --
/*
Dbrown, Dibrown demand rate brownian movement generators
Dbufrd buffer demand ugen
DbufTag demand rate tag system on a buffer
Dbufwr buffer demand ugen
Demand demand results from demand rate ugens
DemandEnvGen demand rate envelope generator
Dfsm demand rate finite state machine
Dgeom demand rate geometric series ugen
Donce
Dpoll print the current output value of a demand rate UGen
Drand, Dxrand demand rate random sequence generators
Dseq demand rate sequence generator
Dser demand rate sequence generator
Dseries demand rate arithmetic series ugen
Dshuf demand rate random sequence generator
Dstutter demand rate input replicator
Dswitch demand rate generator for embedding different inputs
Dswitch1 demand rate generator for switching between inputs
Dtag demand rate tag system
DUGen
Duty demand results from demand rate ugens
Dwhite, Diwhite demand rate white noise random generators
ListDUGen
TDuty demand results as trigger from demand rate ugens
// Also:
Dbrown2 demand rate brownian movement with Gendyn distributions
*/